package edu.ccut.saturn.component.loader;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.Element;
import org.osgi.framework.Bundle;

import edu.ccut.saturn.component.ILogger;
import edu.ccut.saturn.component.IValidate;
import edu.ccut.saturn.component.IValidateInfo;
import edu.ccut.saturn.component.IValidateUseInfo;
import edu.ccut.saturn.component.SaturnComponentException;
import edu.ccut.saturn.component.SaturnLoggerManager;
import edu.ccut.saturn.component.impl.SaturnValidateInfo;
import edu.ccut.saturn.component.impl.SaturnValidateUseInfo;
import edu.ccut.saturn.component.utils.LoaderUtils;
import edu.ccut.saturn.component.xml.Dom4jUtils;

/**
 * 验证组件的加载类
 * 
 * @author 温建
 * 
 * @version 1.0 2008.9.03
 * 
 * @see IValidateUseInfo
 * @see IValidateInfo
 * 
 */
// TODO:将验证组件和验证应用混到一起了
public class ValidateLoader {

	private static final String LOGGER_KEY_LOAD_BUNDLE = SaturnLoggerManager.LOGGER_KEY_LOAD_BUNDLE;

	protected static ILogger logger = SaturnLoggerManager.getInstance()
			.getSaturnLogger(LOGGER_KEY_LOAD_BUNDLE);

	public static final String KEY_NODETYPE_VALIDATE = "validate";

	public static final String KEY_VALIDATE_KEY = "key";

	public static final String KEY_VALIDATE_NAME = "name";

	public static final String KEY_VALIDATE_CLASS = "class";

	public static final String KEY_VALIDATE_DESCRIPTION = "description";

	public static final String KEY_VALIDATE_ARGS = "args";

	public static final String KEY_VALIDATE_ARGS_ARG = "arg";

	public static final String KEY_VALIDATES_VALUEKEY = "value-key";

	public static final String KEY_VALIDATES_CLASSTYPE = "classType";

	public static final String KEY_VALIDATES_ISVALIDATE = "isValidate";

	public static final String KEY_VALIDATE_JSMETHODNAME = "js-method-name";

	public static final String KEY_VALIDATE_CUSTOMJSMETHOD = "custom-js-method";

	public static final String KEY_VALIDATE_EVENTINFOS = "eventInfos";

	public static final String KEY_EVENTINFOS_EVENTINFO = "eventInfo";

	public static final String KEY_VALIDATE_ERRORINFOS = "errorInfos";

	public static final String KEY_ERRORINFOS_ERRORINFO = "errorInfo";

	public static final String KEY_ERRORINFO_KEY = "key";

	public static final String KEY_VALIDATE_VALIDATETYPE = "validateType";

	/**
	 * 加载应用验证组件的结点信息
	 * 
	 * @param bundle
	 * @param validates
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static IValidateUseInfo loadValidateUseInfo(Bundle bundle,
			Element validates) {

		SaturnValidateUseInfo validateUseInfo = null;

		if (validates != null) {
			String valueKey = validates.attributeValue(KEY_VALIDATES_VALUEKEY)
					.trim();

			String classType = validates
					.attributeValue(KEY_VALIDATES_CLASSTYPE).trim();

			String isValidateText = validates.attributeValue(
					KEY_VALIDATES_ISVALIDATE).trim();

			boolean isValidate = false;

			if ("true".equalsIgnoreCase(isValidateText)) {
				isValidate = true;
			}
			List<Element> validateElements = validates
					.elements(KEY_NODETYPE_VALIDATE);

			List<IValidateInfo> validateInfos = loadValidateInfo(bundle,
					validateElements);

			validateUseInfo = new SaturnValidateUseInfo(valueKey, classType,
					isValidate, validateInfos);
		}
		return validateUseInfo;
	}

	/**
	 * 加载验证组件的配置文件信息
	 * 
	 * @param bundle
	 * @param filePath
	 * @return
	 * @throws SaturnComponentException
	 */
	public static IValidateInfo loadValidateInfo(Bundle bundle, String filePath)
			throws SaturnComponentException {

		InputStream in = null;

		try {
			URL url = bundle.getEntry(filePath);
			logger
					.debug("START LOAD VALIDATE \"" + url
							+ "\"");

			in = url.openStream();
			return loadValidateInfo(bundle, in);

		} catch (IOException e) {
			logger.error("Found IOException! load file failed in bundle [key="
					+ bundle.getSymbolicName() + "] with file name ["
					+ filePath + "]", e);

			throw new SaturnComponentException(
					"load file failed in bundle [key="
							+ bundle.getSymbolicName() + "] with file name ["
							+ filePath + "]", e);

		} catch (Exception e) {
			throw new SaturnComponentException("load file failed in bundle[key="
					+ bundle.getSymbolicName() + "] with file name ["
					+ filePath + "]", e);

		} finally {

			try {

				if (in != null) {
					in.close();
				}

			} catch (IOException e) {
				logger.error("file input stream close error!", e);
			}
			logger.debug("END LOAD VALIDATE");
			logger.debug("");
		}
	}

	/**
	 * 加载验证组件的配置文件信息
	 * 
	 * @param bundle
	 * @param in
	 * @return
	 * @throws ClassNotFoundException
	 * @throws SaturnComponentException
	 */
	@SuppressWarnings("unchecked")
	public static IValidateInfo loadValidateInfo(Bundle bundle, InputStream in)
			throws ClassNotFoundException, SaturnComponentException {

		Document document = Dom4jUtils.getDocment(in);
		Element root = document.getRootElement();
		Element validateNode = root.element(KEY_NODETYPE_VALIDATE);
		return loadValidateInfo(bundle, validateNode);
	}

	/**
	 * 加载应用验证组件结点记录的验证组件信息
	 * 
	 * @param bundle
	 * @param validates
	 * @return
	 */
	public static List<IValidateInfo> loadValidateInfo(Bundle bundle,
			List<Element> validates) {

		List<IValidateInfo> validateInfos = new LinkedList<IValidateInfo>();

		for (Element validate : validates) {
			IValidateInfo validateInfo = loadValidate(bundle, validate);

			if (validateInfo != null) {
				validateInfos.add(validateInfo);
			}
		}
		return validateInfos;
	}

	/**
	 * 加载应用验证组件结点记录的验证组件信息。
	 * 当用户没有指定触发事件时默认添加“onblur”事件；当用户没有指定验证类型时，默认指定为“validateType_error”类型。
	 * 
	 * @param bundle
	 * @param validateNode
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static IValidateInfo loadValidate(Bundle bundle, Element validateNode) {

		String key = validateNode.elementTextTrim(KEY_VALIDATE_KEY);
		String name = validateNode.elementTextTrim(KEY_VALIDATE_NAME);
		String description = validateNode
				.elementTextTrim(KEY_VALIDATE_DESCRIPTION);

		Element errorInfos = validateNode.element(KEY_VALIDATE_ERRORINFOS);

		if (errorInfos == null) {
			return null;
		}
		List<Element> errorInfoNodes = errorInfos
				.elements(KEY_ERRORINFOS_ERRORINFO);

		Map<String, String> errorInfoMap = null;
		if (errorInfoNodes != null && errorInfoNodes.size() > 0) {
			errorInfoMap = new HashMap<String, String>();
			getErrorMap(errorInfoNodes, errorInfoMap);
		}

		Element eventInfos = validateNode.element(KEY_VALIDATE_EVENTINFOS);

		if (eventInfos == null) {
			return null;
		}
		List<Element> eventInfoNodes = eventInfos
				.elements(KEY_EVENTINFOS_EVENTINFO);

		List<String> eventInfoList = null;
		if (eventInfoNodes != null && eventInfoNodes.size() > 0) {
			eventInfoList = new ArrayList<String>();

			for (Element eventInfoNode : eventInfoNodes) {
				String event = eventInfoNode.getTextTrim();

				if (event != null && !"".equals(event)) {
					eventInfoList.add(event);
				}
			}
		}
		String validateType = validateNode
				.elementTextTrim(KEY_VALIDATE_VALIDATETYPE);

		Element argsNode = validateNode.element(KEY_VALIDATE_ARGS);
		List<Element> args = argsNode.elements(KEY_VALIDATE_ARGS_ARG);
		String jsMethodName = validateNode
				.elementTextTrim(KEY_VALIDATE_JSMETHODNAME);

		String custemJSMethodInfo = validateNode
				.elementTextTrim(KEY_VALIDATE_CUSTOMJSMETHOD);

		List<String> arguments = null;

		if (args != null) {
			arguments = loadValidateArgs(bundle, args);
		}

		if (validateType == null || "".equals(validateType)) {
			validateType = IValidateInfo.VALIDATETYPE_ERROR;
		}

		if (eventInfoList == null) {
			eventInfoList = new ArrayList<String>();
			eventInfoList.add(IValidateInfo.EVENT_ONBLUR);
		}
		//////////////新增class
		String validateClass = validateNode.elementTextTrim(KEY_VALIDATE_CLASS);
		SaturnValidateInfo saturnValidateInfo = new SaturnValidateInfo(key, name,
				description, errorInfoMap, arguments, validateClass, eventInfoList,
				validateType);

		saturnValidateInfo.setJSMethodName(jsMethodName);
		saturnValidateInfo.setCustemJSMethodInfo(custemJSMethodInfo);
		saturnValidateInfo.setValidateInfoFullkey(key);
		logger.debug("load validate information  key=" + key + "name=" + name
				+ "");

		return saturnValidateInfo;
	}

	/**
	 * 加载验证组件的配置文件信息。其中参数信息、错误信息和验证类型（error或warn）没有加载到<code>IValidateInfo</code>实例，
	 * 他们需要在应用验证组件时加载。
	 * 
	 * @param bundle
	 * @param validateNode
	 * @return
	 * @throws ClassNotFoundException
	 */
	@SuppressWarnings("unchecked")
	public static IValidateInfo loadValidateInfo(Bundle bundle,
			Element validateNode) throws ClassNotFoundException {

		String key = validateNode.elementTextTrim(KEY_VALIDATE_KEY);
		String name = validateNode.elementTextTrim(KEY_VALIDATE_NAME);
		String classType = validateNode.elementTextTrim(KEY_VALIDATE_CLASS);
		String description = validateNode
				.elementTextTrim(KEY_VALIDATE_DESCRIPTION);

		Element errorInfos = validateNode.element(KEY_VALIDATE_ERRORINFOS);
		if (errorInfos == null) {
			return null;
		}
		List<Element> errorInfoNodes = errorInfos
				.elements(KEY_ERRORINFOS_ERRORINFO);

		Map<String, String> errorInfoMap = null;
		if (errorInfoNodes != null && errorInfoNodes.size() > 0) {
			errorInfoMap = new HashMap<String, String>();
			getErrorMap(errorInfoNodes, errorInfoMap);
		}
		// ///////////////////////////////////////////////////
		Element eventInfos = validateNode.element(KEY_VALIDATE_EVENTINFOS);
		
		List<String> eventInfoList = null;
		
		if (eventInfos != null) {
			
			List<Element> eventInfoNodes = eventInfos
					.elements(KEY_EVENTINFOS_EVENTINFO);

			if (eventInfoNodes != null && eventInfoNodes.size() > 0) {
				eventInfoList = new ArrayList<String>();

				for (Element eventInfoNode : eventInfoNodes) {
					String event = eventInfoNode.getTextTrim();

					if (event != null && !"".equals(event)) {
						eventInfoList.add(event);
					}
				}
			}

			if (eventInfoList == null) {
				eventInfoList = new ArrayList<String>();
				eventInfoList.add(IValidateInfo.EVENT_ONBLUR);
			}
		}

		Element argsNode = validateNode.element(KEY_VALIDATE_ARGS);
		List<Element> args = argsNode.elements(KEY_VALIDATE_ARGS_ARG);

		List<String> arguments = null;

		if (args != null) {
			arguments = loadValidateArgs(bundle, args);
		}

		// /////////////////////////////////////////////////
		String jsMethodName = validateNode
				.elementTextTrim(KEY_VALIDATE_JSMETHODNAME);

		String custemJSMethodInfo = validateNode
				.elementTextTrim(KEY_VALIDATE_CUSTOMJSMETHOD);

		Class<?> type = LoaderUtils.loadClass(bundle, classType);
		Class<? extends IValidate> validateType = null;

		if (type != null) {
			validateType = type.asSubclass(IValidate.class);
		}

		SaturnValidateInfo saturnValidateInfo = new SaturnValidateInfo(key, name,
				description, errorInfoMap, validateType, arguments, classType,
				//TODO    lijialun      变量 
				eventInfoList, "validateType_error");

		saturnValidateInfo.setJSMethodName(jsMethodName);
		saturnValidateInfo.setCustemJSMethodInfo(custemJSMethodInfo);
		saturnValidateInfo.setValidateInfoFullkey(bundle.getSymbolicName()
				+ "::" + key);

		logger.debug("load validate information  key=" + key + "name=" + name
				+ "");

		return saturnValidateInfo;
	}

	private static void getErrorMap(List<Element> errorInfoNodes,
			Map<String, String> errorInfoMap) {

		for (Element errorInfoNode : errorInfoNodes) {
			String key = errorInfoNode.attributeValue(KEY_ERRORINFO_KEY);
			String value = errorInfoNode.getTextTrim();

			errorInfoMap.put(key, value);
		}
	}

	private static List<String> loadValidateArgs(Bundle bundle,
			List<Element> args) {

		List<String> arguments = new LinkedList<String>();

		for (Element arg : args) {
			String valueKey = arg.getText();
			logger.debug("load validate arguments information value="
					+ valueKey);

			arguments.add(valueKey);
		}

		return arguments;
	}
}
